// 可继续遍历类型
const mapTag = "[object Map]";
const setTag = "[object Set]";
const objectTag = "[object Object]";
const arrTag = "[object Array]";
const deepType = [mapTag, setTag, objectTag, arrTag];

// 不可继续遍历类型
const numberTag = "[object Number]";
const boolTag = "[object Boolean]";
const strTag = "[object String]";
const regTag = "[object RegExp]";
const symbolTag = "[object Symbol]";
const dateTag = "[object Date]";
const funcTag = "[object Function]";

function getType(obj) {
  return Object.prototype.toString.call(obj);
}

function getCloneObj(target, typeTag) {
  /*switch (typeTag) {
    case numberTag:
    case boolTag:
    case strTag:
    case regTag:
    case dateTag:
      return new target.constructor(target);
    case arrTag:
      return [];
    case mapTag:
      return new Map();
    case setTag:
      return new Set();
    case objectTag:
      return Object.create(Object.getPrototypeOf(target));
    default:
      return new target.constructor(target);
  }*/
  if(typeTag === objectTag) {
    const allDesc = Object.getOwnPropertyDescriptors(target);
    return Object.create(Object.getPrototypeOf(target),allDesc);
  }else{
    return new target.constructor(target);
  }
}

function deepClone(target, hash = new WeakMap()) {
  if (typeof target !== 'object') {
    return target;
  }
  if (hash.has(target)) {
    return hash.get(target)
  }
  const typeTag = getType(target);
  const cloneObj = getCloneObj(target, typeTag);
  hash.set(target, cloneObj);
  if (deepType.includes(typeTag)) {
    if (typeTag === mapTag) {
      target.forEach((value, key) => {
        cloneObj.set(key, deepClone(value, hash));
      })
    }
    if (typeTag === setTag) {
      target.forEach(value => {
        cloneObj.add(deepClone(value, hash))
      })
    }
    if ([arrTag, objectTag].includes(typeTag)) {
      for (const key in target) {
        if (target.hasOwnProperty(key)) {
          cloneObj[key] = deepClone(target[key], hash);
        }
      }
    }
  }
  return cloneObj;
}
